
#include    "acel/unix_addr.hpp"

namespace   acel
{
u_long  hash_pjw (const char *str, size_t len);


// Set a pointer to the address.
void
UNIX_Addr::setAddr (void *addr, int len)
{

    this->Addr::base_set (AF_UNIX, len);
    memcpy ((void *) &this->_unix_addr,
            (void *) addr,
            len);
}

// Return a pointer to the underlying address.

void *
UNIX_Addr::getAddr (void) const
{
    return (void *) &this->_unix_addr;
}

// Transform the string into the current addressing format.

int
UNIX_Addr::fromString (const char addr[])
{
    strncpy (this->_unix_addr.sun_path, addr,
            sizeof this->_unix_addr.sun_path);
    return 0;
}

// Transform the current address into string format.

int
UNIX_Addr::toString (char s[], size_t len) const
{
    strncpy (s,
            this->_unix_addr.sun_path,
            len);
    return 0;
}

u_long
UNIX_Addr::hash (void) const
{
    return hash_pjw (this->_unix_addr.sun_path, strlen(this->_unix_addr.sun_path));
}

// Do nothing constructor.

UNIX_Addr::UNIX_Addr (void)
    : Addr (AF_UNIX, sizeof this->_unix_addr)
{
    (void) memset ((void *) &this->_unix_addr,
            0,
            sizeof this->_unix_addr);

    this->_unix_addr.sun_family = AF_UNIX;
}

int
    UNIX_Addr::set (const UNIX_Addr &sa)
    {
        if (sa.getType () == AF_ANY)
            (void) memset ((void *) &this->_unix_addr,
                    0,
                    sizeof this->_unix_addr);
        else
            strcpy (this->_unix_addr.sun_path,
                    sa._unix_addr.sun_path);

        this->_unix_addr.sun_family = AF_UNIX;
        this->base_set (sa.getType (), sa.getSize ());

        return 0;
    }

// Copy constructor.

UNIX_Addr::UNIX_Addr (const UNIX_Addr &sa)
    : Addr (AF_UNIX, sa.getSize ())
{
    this->set (sa);
}

int
UNIX_Addr::set (const sockaddr_un *un, int len)
{
    (void) memset ((void *) &this->_unix_addr, 0,
            sizeof this->_unix_addr);
    this->_unix_addr.sun_family = AF_UNIX;
    strcpy (this->_unix_addr.sun_path, un->sun_path);
    this->base_set (AF_UNIX, len);
    return 0;
}

UNIX_Addr::UNIX_Addr (const sockaddr_un *un, int len)
{
    this->set (un, len);
}

int
UNIX_Addr::set (const char rendezvous_point[])
{
    (void) memset ((void *) &this->_unix_addr,
            0,
            sizeof this->_unix_addr);
    this->_unix_addr.sun_family = AF_UNIX;
    (void) strncpy (this->_unix_addr.sun_path,
            rendezvous_point,
            sizeof this->_unix_addr.sun_path);

    this->Addr::base_set (AF_UNIX,
            sizeof this->_unix_addr -
            sizeof (this->_unix_addr.sun_path) +
            strlen (this->_unix_addr.sun_path));
    return 0;
}

// Create a Addr from a UNIX pathname.

UNIX_Addr::UNIX_Addr (const char rendezvous_point[])
{
    this->set (rendezvous_point);
}

u_long
hash_pjw (const char *str, size_t len)
{
  u_long hash = 0;

  for (size_t i = 0; i < len; i++)
    {
      const char temp = str[i];
      hash = (hash << 4) + (temp * 13);

      u_long g = hash & 0xf0000000;

      if (g)
        {
          hash ^= (g >> 24);
          hash ^= g;
        }
    }

  return hash;
}

}   // namespace acel
